package com.hejx.web.interceptor;

import com.hejx.common.Result;
import com.hejx.dto.OnlineUser;
import com.hejx.model.Resource;
import com.hejx.model.Role;
import com.hejx.util.JsonUtils;
import com.hejx.util.ResultUtil;
import com.hejx.web.annotation.hasAnyRoles;
import com.hejx.web.annotation.hasPermission;
import com.hejx.web.annotation.hasRole;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Map;

/**
 * Created by 追风少年
 *
 * @email doubihah@foxmail.com
 * @create 2018/1/5 14:49
 **/
@Slf4j
public class PermissionInterceptor extends HandlerInterceptorAdapter {

    private static final String noAuthUrl = "/test/403.html";
    private static final String SESSION_USER_KEY = "user";

    /**
     * 在业务处理器处理请求之前被调用
     * 如果返回false
     * 从当前的拦截器往回执行所有拦截器的afterCompletion(),再退出拦截器链
     * 如果返回true
     * 执行下一个拦截器,直到所有的拦截器都执行完毕
     * 再执行被拦截的Controller
     * 然后进入拦截器链,
     * 从最后一个拦截器往回执行所有的postHandle()
     * 接着再从最后一个拦截器往回执行所有的afterCompletion()
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        OnlineUser onlineUser = (OnlineUser) request.getSession().getAttribute(SESSION_USER_KEY);
        String servletPath = request.getServletPath();
        Map parameterMap = request.getParameterMap();
        if(onlineUser == null){
            log.info("someone visit {}, but no login, parameter:{}", servletPath, JsonUtils.objectToJson(parameterMap));
            noAuth(request, response);
            return false;
        }else{ // 验证用户是否拥有资源权限, 要求满足1个就可以访问
            if(handler instanceof HandlerMethod){
                HandlerMethod handlerMethod = (HandlerMethod) handler;
                boolean auth = checkMethodPermission(handlerMethod, onlineUser);
                if(!auth){ // 没有权限
                    log.info("{} visit {}, bug no permission. parameter:{}", onlineUser.getUsername(), servletPath, JsonUtils.objectToJson(parameterMap));
                    noAuth(request, response);
                    return false;
                }
            }
            return true;
        }

    }

    /**
     * 没有授权访问
     */
    private void noAuth(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String servletPath = request.getServletPath();
        if (servletPath.endsWith(".html")) { // 页面跳转的请求
            response.sendRedirect(noAuthUrl);
        } else { // AJAX的请求
            Result error = ResultUtil.error("没有访问权限，如需要访问，请联系管理员");
            response.setHeader("Content-Type", "application/json");
            response.getWriter().print(JsonUtils.objectToJson(error));
        }
    }

    /**
     * 检查用户是否拥有方法权限
     * @return 是否可以访问:true:有权访问 false:无权访问
     */
    private boolean checkMethodPermission(HandlerMethod handlerMethod, OnlineUser onlineUser){
        boolean auth = true; // 默认允许访问
        hasRole hasRole = handlerMethod.getMethodAnnotation(hasRole.class);
        if(hasRole != null){ // 需要验证角色
            auth = false;
            if(this.checkRole(onlineUser,hasRole)) return true;
        }
        hasAnyRoles hasAnyRoles = handlerMethod.getMethodAnnotation(hasAnyRoles.class);
        if(hasAnyRoles != null){
            auth = false;
            if(this.checkRole(onlineUser,hasAnyRoles)) return true;
        }
        hasPermission hasPermission = handlerMethod.getMethodAnnotation(hasPermission.class);
        if(hasPermission != null){
            auth = false;
            if(this.checkPermission(onlineUser,hasPermission)) return true;
        }
        return auth;
    }

    /**
     * 检查用户是否具有指定角色
     * @return
     */
    private boolean checkRole(OnlineUser user, hasRole hasRole){
        String roleName = hasRole.value();
        if(user == null || CollectionUtils.isEmpty(user.getRoles())) return false;
        for (int i = 0; i < user.getRoles().size(); i++) {
            Role role = user.getRoles().get(i);
            if(roleName.equals(role.getRole())){
                return true;
            }
        }
        log.debug("checkRole finished, {} has not role, authRole:{}"
                ,user.getUsername(),roleName);
        return false;
    }

    /**
     * 验证当前用户是否拥有以下任意一个角色
     */
    private boolean checkRole(OnlineUser user, hasAnyRoles hasAnyRoles){
        if(user == null || CollectionUtils.isEmpty(user.getRoles())) return false;
        String[] authRoles = hasAnyRoles.value();
        for (int i = 0; i < authRoles.length; i++) {
            String authRole = authRoles[i];
            for (Role role : user.getRoles()) {
                if(role.getRole().equals(authRole)){
                    return true;
                }
            }
        }
        log.debug("checkRole finished, {} has not role, authRole:{}"
                ,user.getUsername(),authRoles.toString());
        return false;
    }

    /**
     * 验证当前用户是否拥有指定权限
     */
    private boolean checkPermission(OnlineUser user, hasPermission hasPermission){
        if(user == null || CollectionUtils.isEmpty(user.getRoles())) return false;
        String permission = hasPermission.value();
        for (int i = 0; i < user.getResources().size(); i++) {
            Resource resource = user.getResources().get(i);
            if(permission.equals(resource.getPermission())){
                return true;
            }
        }
        log.debug("checkPermission finished, {} has not permission, authPermission:{}"
                ,user.getUsername(),permission);
        return false;
    }



}
